package com.hitqz.robot.driver.communication;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 通用 UDP 客户端
 */
@Slf4j
public class UdpClient {

    private final String name;
    private Channel channel;
    private final ChannelInitializer<NioDatagramChannel> initializer;
    private final EventLoopGroup group;
    private final AtomicBoolean isReconnecting = new AtomicBoolean(false);

    // 构造函数，允许传递ChannelInitializer
    public UdpClient(String name,ChannelInitializer<NioDatagramChannel> initializer) {
        this.name = name;
        this.initializer = initializer;
        this.group = new NioEventLoopGroup();
    }

    public void bind() throws InterruptedException {
        Bootstrap b = new Bootstrap();

        b.group(group)
                .channel(NioDatagramChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .option(ChannelOption.SO_BROADCAST, true)
                .handler(initializer);

        try {
            //随机绑定一个端口
            ChannelFuture future = b.bind(0).sync();
            int localPort = ((NioDatagramChannel) future.channel()).localAddress().getPort();
            log.info("[udp-client][{}] local udp port:{}",name,localPort);
            channel = future.channel();
            channel.closeFuture().sync();
        } catch (Throwable t) {
            log.error("[udp-client][{}] client start error,{}",name, t.getMessage());
            shutdown();
        }
    }

    public void bindAsync() {
        Bootstrap b = new Bootstrap();

        b.group(group)
                .channel(NioDatagramChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .option(ChannelOption.SO_BROADCAST, true)
                .handler(initializer);

        b.bind(0).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                int localPort = ((NioDatagramChannel) future.channel()).localAddress().getPort();
                log.info("[udp-client][{}] bind {}", name, localPort);
                isReconnecting.set(false);
                channel = future.channel();
            } else {
                int localPort = ((NioDatagramChannel) future.channel()).localAddress().getPort();
                log.info("[udp-client][{}] bind {} failed, retry..", name, localPort);
                future.channel().eventLoop().schedule(this::bindAsync, 5, TimeUnit.SECONDS); // 5秒后重连
            }
        });
    }

    private synchronized void reconnect() {
        if (isReconnecting.get()) {
            return;  // 如果已经在重连，不再继续
        }
        isReconnecting.set(true);
        bindAsync();  // 异步重新绑定
    }

    public void sendMessage(byte[] message, String ip, int port) {
        try {
            InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, port);
            if (readyToSend()) {
                // 发送消息
                channel.writeAndFlush(new DatagramPacket(Unpooled.wrappedBuffer(message), inetSocketAddress)).sync();
                if (log.isDebugEnabled()) {
                    log.debug("[udp-client][{}] send to [{}:{}]:{}",name, ip, port, bytesToHexString(message));
                }
            } else {
                log.warn("[udp-client][{}] channel is not active, message not sent.",name);
                reconnect();
            }
        } catch (Exception e) {
            log.error("[udp-client][{}] send message error", name,e);
        }
    }

    public boolean readyToSend() {
        return channel != null && channel.isActive();
    }

    public void shutdown() throws InterruptedException {
        if (channel != null) {
            channel.close().sync();
        }
        group.shutdownGracefully().sync();
        log.info("[udp-client][{}] shutdown gracefully",name);
    }

    // 辅助方法，将字节数组转换为十六进制字符串
    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b)).append(" ");
        }
        return sb.toString();
    }
}